//javascript　The Good　Parts
var log = function(msg){
	console.log(msg);
};
(function(log){
	var add = function(a,b){return a+b;}
	var obj = {
		value:0,
		//function as method　作为方法调用
		increase:function(num){
			this.value += typeof num === 'number' ? num : 1;
		}
	};

	obj.increase();
	log('increase ' + obj.value);

	//function as function 作为函数调用
	obj.double = function(){
		var me = this;
		log(me); //obj
		var helper = function(){
			log(this); //global object, can see node version
			me.value += me.value;
		};
		helper();
	}

	obj.double();
	log('double ' + obj.value);

	//function as constructor 作为构造器调用
	var Quo = function(string){
		this.state = string;
	}
	Quo.prototype.getState = function(){
		return this.state;
	}
	//new an object with constructor,deprecated
	var myQuo = new Quo('confused');
	log('state ' + myQuo.getState());
	
	//js是一门函数式的面向对象编程语言，所以函数可以拥有方法
	//apply可以让我们去调用函数，第一个参数绑定给this值，第二个是参数数组,具体看后面代码
	var array = [3,4];
	var sum = add.apply(null,array);
	log('apply ' + sum);

	var stateObj = {
		state:'OK',
	};
	var state = Quo.prototype.getState.apply(stateObj);
	log(state);

	var sum = function(){
		var i,sum = 0;
		//函数调用时，会得到一个参数数组arguments,但其实它不是真正的数组，这是一个设计上的错误
		for(i = 0; i < arguments.length; i++){
			sum += arguments[i];
		}
	//一个函数总是会返回值，如果未指定，则返回undefined，这就是在node　REPL中经常见到undefined的原因
		return sum;
	}
	log(sum(1,2,4,8));
	
	//学会try-catch代码
	//作用域
	var foo = function(){
		var a = 'foo_a', b = 'foo_b';
		var bar = function(){
			var b = 'bar_b', c = 'bar_c';
			log('in bar 相加之前a ' + a + ', b ' + b);
			a += b + c;
			log('in bar 相加之后a ' + a + ', b ' + b);
			//函数调用结束后，b = bar_b 消失
		};
		bar();
		//a 是相加后的,　b没变
		log('in foo bar() 之后a ' + a + ', b ' + b);
	}

	foo();

	//函数可以访问它被创建时所处的上下文环境，这叫闭包。
	var closure_test = function(nodes){
		for(var i = 0; i < 5; i++){
			nodes[i] = function(){
				console.log(i);
			}
		}
		log('javascript 没有块级作用域，这意味着，for循环外可以访问for循环里的变量: i=' + i);
	};
	var nodes = new Array(5);
	closure_test(nodes);
	log('----------闭包的知识，糟糕的例子----------');
	for(var i = 0; i < nodes.length; i++){
		nodes[i]();
	}

	log('----------array----------');
	var array = [];
	log('新建数组,length ' + array.length);
	array[99] = 'null';
	log('array[99]赋值以后,length　' + array.length);
	log('array[0]  ' + array[0]);
	
	//尾部添加元素 array[array.length] = 'value';
	//you can also use push array.push('value');
	var numbers = [1,2,3,4,5];
	//参数的意思：数组下标，要删除的元素个数
	log('join() ' + numbers.join());
	log('slice ' + numbers.splice(2,1));
	log('numbers ' + numbers);
})(log);

log('------------面试题start------------');
(function(){
	//log('------use strict 必须在第一行，插入几道面试题------');
	//var a = b = 'var from interview';
	//'use strict';
	log('------use strict 必须在第一行，插入几道面试题------');
	var a = log.b = 'var from interview';

	log('增加原型方法,注意不要覆盖原有的');
	Function.prototype.method = function(name,func){

		if(!this.prototype[name]){
			this.prototype[name] = func;
			return this;
		}
	}

	//另有一种写法
	String.prototype.repeatify = String.prototype.repeatify || function(times) {
	   var str = '';
	   for (var i = 0; i < times; i++) {
		  str += ' ' + this;
	   }
	   return str;
	};

	String.prototype.repeatify = String.prototype.repeatify || function(){log('cannot override');}
	log('hello'.repeatify(3));
	log('hello'.repeatify());


	log('----------how this work----------');
	var fullname = 'global fullname';
	var obj = {
	   fullname: 'obj fullname',
	   prop: {
		  fullname: 'prop fullname',
		  getFullname: function() {
			 return this.fullname;
		  }
	   }
	};

	console.log('obj log: ' + obj.prop.getFullname());
	 
	var test = obj.prop.getFullname;
	 
	 //undefined, 要在浏览器中运行，this指向全局变量window,输出global fullname;
	 //以此说明，在node中与在browser中运行js,还是有些不一样的。
	console.log('test log: ' + test());

	//test如何输出obj fullname、prop　fullname?
	//使用call、apply改变this指向的对象
	log('----------use call and apply to 改变函数上下文----------');
	log('call ' + test.call(obj));
	log('apply ' + test.apply(obj.prop));
})(log);

	//a is not defined
	//log('a ' + a);
	log('-----------');
	log('log.b ' + log.b);

	function f(){
	//this指向全局对象,则!this为false
		log('normal mode !this = ' + !this);
	}

	function sf(){
		'use strict';
	//严格模式下禁止this指向全局对象,则this为unfined
		log('strict mode !this = ' + !this);
	}
	f();sf();
log('------------面试题end------------');

//2016/7/25 再次阅读有新收获
console.log('//记忆：函数把之前运算的结果存储起来，从而避免重复的计算，这种优化叫记忆。');
var memorizer = function(memo, fundamental) {
  var shell = function(n) {
    var result = memo[n];
    if(typeof result !== 'number') {
      result = fundamental(shell, n);
      memo[n] = result;
    }
    console.log(memo)
    return result;
  }
  return shell;
} 

var fibonacci = memorizer([0,1], function(shell, n) {
  if(typeof n === 'undefined') {
    throw new Error('no arguments');
  }
  if(n <= 2) {
    return 1;
  }
  return shell(n-1) + shell(n-2);
});

fibonacci(13);


console.log('//prevent modification');
(function(){
'use strict';
var extend = {
	name: 'Nicolas',
};
console.log('preventExtensions');
Object.preventExtensions(extend);
console.log('//isExtensible ' + Object.isExtensible(extend));

delete extend.name;
console.log('after delete ');
console.log(extend);
//extend.age = 25;

var seal = {
	name: 'seal'
};

Object.seal(seal);
console.log('//isSealed ' + Object.isSealed(seal));
seal.name = 'modify name';
console.log('after modify');
console.log(seal);
//delete seal.name;

var freeze = {
	name: 'freeze',
};

Object.freeze(freeze);
console.log('//isFrozen ' + Object.isFrozen(freeze));
freeze.name = 'modify';


}());

